#pragma once

#include <memory>

#include "Containers/Vector.h"
#include "Containers/String.h"

#include "ScriptCompiler/Token/FilePosition.h"
#include "ScriptCompiler/SemanticAnalyzer/StatementType.h"

struct SDecoratedTypeDescription;
class CFunctionContext;
class CVariableDescription;
class CParameterDescription;
class CPropertyDescription;
class CGSharpFieldDescription;
class CNamespaceDescription;
class CTypeDescription;
class CFunctionDescription;
class CExpressionContext;

class CStatementContext :public std::enable_shared_from_this<CStatementContext>
{
public:

    //InParentSentence : 
    CStatementContext(
        std::weak_ptr<CFunctionContext> InFunction,
        std::weak_ptr<CStatementContext> InParentSentence
        ,EStatementType InStatementType
        );

    std::shared_ptr<CFunctionContext> GetFunction();

    std::shared_ptr<CStatementContext> GetParentSentence();

    std::shared_ptr<CExpressionContext> NewExpression(std::shared_ptr<CExpressionContext> InParentExpression);
    

    //test is a variable name is valid
    //return 1. pre-defined variable position ,if not valid or
    //       2. nullptr, if valid
    TOptional<SScriptFilePosition> TestVariableName(const CString& InVariableName);

    std::shared_ptr<CVariableDescription> AddVariable(SDecoratedTypeDescription InVaraibleType,const CString& InVariableName);
    
    //Ocuppy a anonymous variable,mark the variable is used,cant use it in same sentence again
    //if not exist, add a anonymous variable
    //Ocuppied anonymous variables cant be ocuppied again in the same sentence
    std::shared_ptr<CVariableDescription> OcuppyOrAddAnonymousVariable(SDecoratedTypeDescription InVaraibleType);

    bool IsAnonymousVariableOcuppied(std::shared_ptr<CVariableDescription> InVariable);

    std::shared_ptr<CVariableDescription> FindVariableByName(const CString& InVariableName);

    std::shared_ptr<CParameterDescription> FindParameterByName(const CString& InParameterName);

    std::shared_ptr<CGSharpFieldDescription> FindFieldByName(const CString& InFieldName);

    TVector<std::shared_ptr<CFunctionDescription>> FindFunctionsByName(const CString& InFieldName);

    std::shared_ptr<CPropertyDescription> FindPropertyByName(const CString& InPropertyName);

    std::shared_ptr<CNamespaceDescription> FindNamespaceByName(const CString& InNamespaceName);

    std::shared_ptr<CTypeDescription> FindClassByName(const CString& InClassName);

    std::shared_ptr<CFunctionDescription> FindFunctionByNameAndType(const CString& InFunctionName, SDecoratedTypeDescription InFunctionType);

    //test is a function name is valid
    bool IsPosibleFunction(const CString& InFunctionName);


protected:
    EStatementType Type;
    std::weak_ptr<CFunctionContext> Function;

    std::weak_ptr<CStatementContext> ParentSentence;
    TVector<std::shared_ptr<CStatementContext>> ChildSentences;
    friend class CFunctionContext;
    
    //Used anonymous variables
    //when begin derivation a  sentence
    //all anonymous variables will be treated as a uninitialized variable
    //must assign a value to it before use it
    TVector<std::shared_ptr<CVariableDescription>> OcuppiedAnonymousVariables;
    void OcuppyAnonymousVariable(std::shared_ptr<CVariableDescription> InVariable);
};